﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace kmer.Helper {
	public static class DateHelper {
		/// <summary>
		/// 返回JavaScript 的 Date Ticks
		/// new Date(ticks);
		/// </summary>
		/// <param name="theDate"></param>
		/// <returns></returns>
		public static double JsTicks(this DateTime theDate) {
			var h = TimeZone.CurrentTimeZone.GetUtcOffset(theDate).Hours;
			return theDate.Subtract(new DateTime(1970, 1, 1, h, 0, 0)).TotalMilliseconds;
		}
		/// <summary>
		/// 判断日期是否是今天
		/// </summary>
		/// <param name="theDate"></param>
		/// <returns></returns>
		public static bool IsToday(this DateTime theDate) {
			var st = DateTime.Now - theDate;
			return st.Days == 0;
		}
		/// <summary>
		/// 判断是否在指定的时间区间内 dateBegin &lt; theDate &lt; dateEnd
		/// 如果 dateBegin 比 dateEnd 大，会自动交换
		/// </summary>
		/// <param name="theDate"></param>
		/// <param name="dateBegin"></param>
		/// <param name="dateEnd"></param>
		/// <param name="isIncludeTheDate">true 表示 dateBegin &lt;= theDate &lt;= dateEnd</param>
		/// <returns></returns>
		public static bool IsBetween(this DateTime theDate, DateTime dateBegin, DateTime dateEnd, bool isIncludeTheDate = false) {
			if (dateBegin > dateEnd) {
				var s = dateEnd;
				dateEnd = dateBegin;
				dateBegin = s;
			}

			return isIncludeTheDate ?
				theDate >= dateBegin && theDate <= dateEnd
				: theDate > dateBegin && theDate < dateEnd;
		}

		/// <summary>
		/// 判断是否不在指定的时间区间内  theDate &lt; dateBegin || theDate &gt; dateEnd
		/// 如果 dateBegin 比 dateEnd 大，会自动交换
		/// </summary>
		/// <param name="theDate"></param>
		/// <param name="dateBegin"></param>
		/// <param name="dateEnd"></param>
		/// <param name="isIncludeTheDate">true theDate &lt;= dateBegin || theDate &gt;= dateEnd </param>
		/// <returns></returns>
		public static bool IsNotBetween(this DateTime theDate, DateTime dateBegin, DateTime dateEnd, bool isIncludeTheDate = false) {
			if (dateBegin > dateEnd) {
				var s = dateEnd;
				dateEnd = dateBegin;
				dateBegin = s;
			}
			return isIncludeTheDate
				? theDate <= dateBegin || theDate >= dateEnd
				: theDate < dateBegin || theDate < dateEnd;
		}
		/// <summary>
		/// 是否是其中的一天（x年x月x日 0:0:0到24:0:0）
		/// </summary>
		/// <param name="theDate"></param>
		/// <param name="dates"></param>
		/// <returns></returns>
		public static bool IsInTheseDay(this DateTime theDate, params DateTime[] dates) {
			foreach (var date in dates) {
				if (theDate.IsTheSameDay(date)) return true;
			}
			return false;
		}

		/// <summary>
		/// 是否是同一天
		/// </summary>
		/// <param name="theDate"></param>
		/// <param name="date"></param>
		/// <returns></returns>
		public static bool IsTheSameDay(this DateTime theDate, DateTime date) {
			if (theDate.Year != date.Year) {
				return false;
			}
			if (theDate.Month != date.Month) {
				return false;
			}
			return theDate.Day == date.Day;
		}
		/// <summary>
		/// 是否同一个月份（年份和月份都相同）
		/// </summary>
		/// <param name="theDate"></param>
		/// <param name="date"></param>
		/// <returns></returns>
		public static bool IsSameMonth(this DateTime theDate, DateTime date) {
			if (theDate.Year != date.Year) {
				return false;
			} else if (theDate.Month != date.Month) {
				return false;
			}
			return true;
		}
		/// <summary>
		/// 是否是同一个季度（1 2 3，4 5 6，7 8 9，10 11 12）
		/// </summary>
		/// <param name="theDate"></param>
		/// <param name="date"></param>
		/// <returns></returns>
		public static bool IsSameQuarter(this DateTime theDate, DateTime date) {
			if (theDate.Year != date.Year) {
				return false;
			}
			return theDate.Month / 3 == date.Month / 3;
		}

		/// <summary>
		/// 返回一个指定日子的新日期
		/// </summary>
		/// <param name="day">1-31</param>
		/// <returns></returns>
		public static DateTime SetDay(this DateTime date, int day) {
			if (day < 1) {
				day = 1;
			} else {
				int maxDay = DateTime.DaysInMonth(date.Year, date.Month);
				day = Math.Min(day, maxDay);
			}
			return date.AddDays(day - date.Day);
		}
		/// <summary>
		/// 返回一个指定月份的新日期
		/// </summary>
		/// <param name="date"></param>
		/// <param name="month"></param>
		/// <returns></returns>
		public static DateTime SetMonth(this DateTime date, int month) {
			if (month < 1) {
				month = 1;
			} else if (month > 12) {
				month = 12;
			}
			return date.AddMonths(month - date.Month);
		}
		/// <summary>
		/// 返回一个指定小时的新日期
		/// </summary>
		/// <param name="date"></param>
		/// <param name="hour"></param>
		/// <returns></returns>
		public static DateTime SetHour(this DateTime date, int hour) {
			if (hour > 24)
				hour = 24;
			else if (hour < 0)
				hour = 0;
			return date.AddHours(hour - date.Hour);
		}
		/// <summary>
		/// 返回一个指定分钟的新日期
		/// </summary>
		/// <param name="date"></param>
		/// <param name="minute"></param>
		/// <returns></returns>
		public static DateTime SetMinute(this DateTime date, int minute) {
			if (minute > 59)
				minute = 59;
			else if (minute < 0)
				minute = 0;
			return date.AddMinutes(minute - date.Minute);
		}
		/// <summary>
		/// 返回一个指定秒的新日期
		/// </summary>
		/// <param name="date"></param>
		/// <param name="second"></param>
		/// <returns></returns>
		public static DateTime SetSecond(this DateTime date, int second) {
			if (second > 59)
				second = 59;
			else if (second < 0)
				second = 0;
			return date.AddSeconds(second - date.Second);
		}
		/// <summary>
		/// 返回一个指定年份的新日期
		/// </summary>
		/// <param name="date"></param>
		/// <param name="year"></param>
		/// <returns></returns>
		public static DateTime SetYear(this DateTime date, int year) {
			if (year < 1) {
				year = 1;
			}
			return date.AddYears(year - date.Year);
		}
		/// <summary>
		/// 返回一个指定时间的新日期
		/// </summary>
		/// <param name="date"></param>
		/// <param name="hour"></param>
		/// <param name="minute"></param>
		/// <param name="second"></param>
		/// <returns></returns>
		public static DateTime SetTime(this DateTime date, int hour, int minute, int second) {
			return date.SetHour(hour).SetMinute(minute).SetSecond(second);
		}
		///<summary>  
		///取指定日期是一年中的第几周  
		///</summary>  
		///<param   name="dtime">给定的日期</param>  
		///<returns>数字   一年中的第几周</returns>  
		public static int WeekOfYear(this DateTime dtime) {
			int weeknum = 1;
			DateTime tmpdate = DateTime.Parse(dtime.Year.ToString() + "-1" + "-1");
			DayOfWeek firstweek = tmpdate.DayOfWeek;
			//if(firstweek)  
			for (int i = (int)firstweek + 1; i <= dtime.DayOfYear; i = i + 7) {
				weeknum = weeknum + 1;
			}
			return weeknum;

		}
		/// <summary>
		/// 取指定日期是当月的第几周
		/// 周一开始
		/// </summary>
		/// <param name="dtime"></param>
		/// <returns></returns>
		public static int WeekOfMonth(this DateTime dtime) {
			DateTime tmpdate = DateTime.Parse(string.Format("{0}-{1}-1", dtime.Year, dtime.Month));
			int i = (int)tmpdate.DayOfWeek;
			if (i == 0) i = 7;
			return (dtime.Date.Day + i - 2) / 7 + 1;
		}
		/// <summary>
		/// 返回指定日期所在月份的天数
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		public static int DaysInMonth(this DateTime date) {
			//DateTime newDate=date.AddMonths(-1);
			return DateTime.DaysInMonth(date.Year, date.Month);
		}
		/// <summary>
		/// 返回农历日期
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		public static ChinaCalendarDate GetChinaDate(this DateTime date) {
			return new ChinaCalendarDate(date);
		}
		/// <summary>
		/// 返回最接近的节气名称
		/// </summary>
		/// <param name="date"></param>
		/// <returns></returns>
		public static string GetSolarTerm(this DateTime date) {
			if (date.Day < 15) {
				return ChinaCalendarDate.solarTerm[date.Month * 2 - 2];
			} else {
				return ChinaCalendarDate.solarTerm[date.Month * 2 - 1];
			}
		}


		public static DateTime? AddDays(this DateTime? date, int days) {
			if (date.HasValue) {
				return date.Value.AddDays(days);
			}
			return date;
		}
		public static DateTime? AddYears(this DateTime? date, int years) {
			if (date.HasValue) {
				return date.Value.AddYears(years);
			}
			return date;
		}
		public static DateTime? AddMonths(this DateTime? date, int months) {
			if (date.HasValue) {
				return date.Value.AddMonths(months);
			}
			return date;
		}
		public static DateTime? AddHours(this DateTime? date, int hours) {
			if (date.HasValue) {
				return date.Value.AddHours(hours);
			}
			return date;
		}
		public static DateTime? AddMinutes(this DateTime? date, int minutes) {
			if (date.HasValue) {
				return date.Value.AddMinutes(minutes);
			}
			return date;
		}
		public static DateTime? AddSeconds(this DateTime? date, int seconds) {
			if (date.HasValue) {
				return date.Value.AddSeconds(seconds);
			}
			return date;
		}

		/// <summary>
		/// 字符串到日期
		/// </summary>
		/// <param name="pStr"></param>
		/// <param name="DefaultValue"></param>
		/// <returns></returns>
		public static DateTime ParseDate(string pStr, DateTime DefaultValue) {
			if (!StringHelper.IsDateTime(pStr))
				return DefaultValue;
			DateTime r;
			if (DateTime.TryParse(pStr, out r)) {
				return r;
			}
			return DefaultValue;
		}
	}
}
namespace System {
	public class ChinaCalendarDate {

		#region 农历数据
		static readonly UInt16[] lunarInfo = new UInt16[]{
0x4bd8,0x4ae0,0xa570,0x54d5,0xd260,0xd950,0x5554,0x56af,0x9ad0,0x55d2,0x4ae0,0xa5b6,0xa4d0,0xd250,0xd295,0xb54f,0xd6a0,0xada2,0x95b0,0x4977,
0x497f,0xa4b0,0xb4b5,0x6a50,0x6d40,0xab54,0x2b6f,0x9570,0x52f2,0x4970,0x6566,0xd4a0,0xea50,0x6a95,0x5adf,0x2b60,0x86e3,0x92ef,0xc8d7,0xc95f,
0xd4a0,0xd8a6,0xb55f,0x56a0,0xa5b4,0x25df,0x92d0,0xd2b2,0xa950,0xb557,0x6ca0,0xb550,0x5355,0x4daf,0xa5b0,0x4573,0x52bf,0xa9a8,0xe950,0x6aa0,
0xaea6,0xab50,0x4b60,0xaae4,0xa570,0x5260,0xf263,0xd950,0x5b57,0x56a0,0x96d0,0x4dd5,0x4ad0,0xa4d0,0xd4d4,0xd250,0xd558,0xb540,0xb6a0,0x95a6,
0x95bf,0x49b0,0xa974,0xa4b0,0xb27a,0x6a50,0x6d40,0xaf46,0xab60,0x9570,0x4af5,0x4970,0x64b0,0x74a3,0xea50,0x6b58,0x5ac0,0xab60,0x96d5,0x92e0,
0xc960,0xd954,0xd4a0,0xda50,0x7552,0x56a0,0xabb7,0x25d0,0x92d0,0xcab5,0xa950,0xb4a0,0xbaa4,0xad50,0x55d9,0x4ba0,0xa5b0,0x5176,0x52bf,0xa930,
0x7954,0x6aa0,0xad50,0x5b52,0x4b60,0xa6e6,0xa4e0,0xd260,0xea65,0xd530,0x5aa0,0x76a3,0x96d0,0x4afb,0x4ad0,0xa4d0,0xd0b6,0xd25f,0xd520,0xdd45,
0xb5a0,0x56d0,0x55b2,0x49b0,0xa577,0xa4b0,0xaa50,0xb255,0x6d2f,0xada0,0x4b63,0x937f,0x49f8,0x4970,0x64b0,0x68a6,0xea5f,0x6b20,0xa6c4,0xaaef,
0x92e0,0xd2e3,0xc960,0xd557,0xd4a0,0xda50,0x5d55,0x56a0,0xa6d0,0x55d4,0x52d0,0xa9b8,0xa950,0xb4a0,0xb6a6,0xad50,0x55a0,0xaba4,0xa5b0,0x52b0,
0xb273,0x6930,0x7337,0x6aa0,0xad50,0x4b55,0x4b6f,0xa570,0x54e4,0xd260,0xe968,0xd520,0xdaa0,0x6aa6,0x56df,0x4ae0,0xa9d4,0xa4d0,0xd150,0xf252,0xd520};
		static readonly int[] sTermInfo = new int[] { 0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758 };
		static readonly string[] Gan = new string[] { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };
		static readonly string[] Zhi = new string[] { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };
		static readonly string[] Animals = new string[] { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };
		public static readonly string[] solarTerm = new string[] { "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" };
		static readonly string[] nStr1 = new string[] { "日", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };
		static readonly string[] nStr2 = new string[] { "初", "十", "廿", "卅", "□" };
		static readonly string[] cmonthName = new string[] { "", "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊" };
		#endregion

		private DateTime _date;
		private int _year;
		private int _month;
		private int _day;
		private bool _isLeap;
		public int Year {
			get {
				return _year;
			}
		}
		public int Month {
			get {
				return _month;
			}
		}
		public int Day {
			get {
				return _day;
			}
		}
		/// <summary>
		/// 农历年份
		/// </summary>
		public string FormatYear {
			get {
				string cY;
				if (this._date.Month < 3) cY = cyclical(this._date.Year - 1900 + 36 - 1);
				else cY = cyclical(this._date.Year - 1900 + 36);
				return cY;
			}
		}
		/// <summary>
		/// 农历月份
		/// </summary>
		public string FormatMonth {
			get {
				if (this._isLeap) {
					return string.Format("闰{0}月", cmonthName[this._month]);
				} else {
					return string.Format("{0}月", cmonthName[this._month]);
				}
			}
		}
		/// <summary>
		/// 农历日
		/// </summary>
		public string ChinaDay {
			get {
				//string s = this.SolarTerm;
				//if (string.IsNullOrEmpty(s)) {
				string s = "";
				switch (this._day) {
					case 10:
						s = "初十"; break;
					case 20:
						s = "二十"; break;
					case 30:
						s = "三十"; break;
					default:
						s = string.Format("{0}{1}", nStr2[(int)Math.Floor((double)this._day / 10)], nStr1[this._day % 10]);
						break;
				}
				//}
				return (s);
			}
		}
		public string FormatDay {
			get {
				if (this._day == 1) {
					return this.FormatMonth;
				} else {
					string s = this.SolarTerm;
					if (string.IsNullOrEmpty(s)) {
						return this.ChinaDay;
					} else {
						return s;
					}
				}
			}
		}
		/// <summary>
		/// 24节气
		/// 如果当日不是节气返回String.Empty
		/// </summary>
		public string SolarTerm {
			get {
				int i = sTerm(this._date.Year, this._date.Month * 2 - 2);
				if (i == this._date.Day) {
					return solarTerm[this._date.Month * 2 - 2];
				} else {
					i = sTerm(this._date.Year, this._date.Month * 2 - 1);
					if (i == this._date.Day) {
						return solarTerm[this._date.Month * 2 - 1];
					}
				}
				return string.Empty;
			}
		}
		/// <summary>
		/// 生肖
		/// </summary>
		public string ChineseZodiac {
			get {
				return Animals[(this._date.Year - 4) % 12];
			}
		}

		#region 日期计算
		/// <summary>
		/// 某年的第n个节气为几日(从0小寒起算)
		/// </summary>
		/// <param name="y">公历年</param>
		/// <param name="n">第n个节气</param>
		/// <returns>UTC</returns>
		static int sTerm(int y, int n) {
			//UTC时间
			long tick = (long)((31556925974.7 * (y - 1900) + (long)sTermInfo[n] * 60000) * 10000);
			var offDate = new DateTime(tick + (new DateTime(1900, 1, 6, 2, 5, 0)).Ticks);
			return (offDate.Day);
		}
		static DateTime sTermDate(int y, int n) {
			//UTC时间
			long tick = (long)((31556925974.7 * (y - 1900) + (long)sTermInfo[n] * 60000) * 10000);
			var offDate = new DateTime(tick + (new DateTime(1900, 1, 6, 2, 5, 0)).Ticks);
			return offDate;
		}
		/// <summary>
		/// 天干地支
		/// </summary>
		/// <param name="num"></param>
		/// <returns></returns>
		string cyclical(int num) {
			return (Gan[num % 10] + Zhi[num % 12]);
		}
		/// <summary>
		/// 返回农历 y年的总天数
		/// </summary>
		/// <param name="y">农历年</param>
		/// <returns></returns>
		int lYearDays(int y) {
			int i, sum = 348;
			for (i = 0x8000; i > 0x8; i >>= 1)
				sum += (lunarInfo[y - 1900] & i) > 0 ? 1 : 0;
			return (sum + leapDays(y));
		}
		/// <summary>
		/// 返回农历 y年闰月的天数
		/// </summary>
		/// <param name="y">农历年份</param>
		/// <returns></returns>
		int leapDays(int y) {
			if (leapMonth(y) > 0)
				return ((lunarInfo[y - 1899] & 0xf) == 0xf ? 30 : 29);
			else return (0);
		}
		/// <summary>
		/// 返回农历 y年闰哪个月 1-12 , 没闰返回 0
		/// </summary>
		/// <param name="y">农历年份</param>
		/// <returns></returns>
		int leapMonth(int y) {
			var lm = lunarInfo[y - 1900] & 0xf;
			return (lm == 0xf ? 0 : lm);
		}
		/// <summary>
		/// 返回农历 y年m月的总天数
		/// </summary>
		/// <param name="y">农历年</param>
		/// <param name="m">农历月</param>
		/// <returns></returns>
		int monthDays(int y, int m) {
			return ((lunarInfo[y - 1900] & (0x10000 >> m)) > 0 ? 30 : 29);
		}
		#endregion

		#region 初始化
		public ChinaCalendarDate()
			: this(DateTime.Now) {
		}
		public ChinaCalendarDate(DateTime objDate) {
			this._date = objDate;
			int i, leap = 0, temp = 0;
			int offset = (int)((double)(new DateTime(this._date.Year, this._date.Month, this._date.Day) - new DateTime(1900, 1, 31)).TotalMilliseconds / 86400000);

			for (i = 1900; i < 2100 && offset > 0; i++) {
				temp = lYearDays(i);
				offset -= temp;
			}

			if (offset < 0) {
				offset += temp;
				i--;
			}

			this._year = i;

			leap = leapMonth(i); //闰哪个月
			this._isLeap = false;

			for (i = 1; i < 13 && offset > 0; i++) {
				//闰月
				if (leap > 0 && i == (leap + 1) && this._isLeap == false) {
					--i;
					this._isLeap = true;
					temp = leapDays(this._year);
				} else {
					temp = monthDays(this._year, i);
				}

				//解除闰月
				if (this._isLeap == true && i == (leap + 1))
					this._isLeap = false;

				offset -= temp;
			}

			if (offset == 0 && leap > 0 && i == leap + 1)
				if (this._isLeap) {
					this._isLeap = false;
				} else {
					this._isLeap = true;
					--i;
				}

			if (offset < 0) {
				offset += temp;
				--i;
			}

			this._month = i;
			this._day = offset + 1;
		}
		#endregion

		#region 一些静态方法
		/// <summary>
		/// 返回某一年（公历）的所有节气的日期
		/// </summary>
		/// <param name="year"></param>
		public static List<DateTime> GetSolarTerms(int year) {
			List<DateTime> list = new List<DateTime>();
			for (int i = 0; i < 12; i++) {
				//list.Add(new DateTime(year, i + 1, sTerm(year, i * 2)));
				//list.Add(new DateTime(year, i + 1, sTerm(year, i * 2 + 1)));
				list.Add(sTermDate(year, i * 2));
				list.Add(sTermDate(year, i * 2 + 1));
			}
			return list;
		}

		#endregion
	}
}
